#include <cppunit/config/SourcePrefix.h>

#include "TestQueryPreprocessor.h"
#include <iostream>
#include <string>

void 
QueryPreprocessorTest::setUp()
{
	cout << "Test QueryPreprocessor setUp()\n";
}

void 
QueryPreprocessorTest::tearDown()
{
}

// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( QueryPreprocessorTest ); // Note 4 


void QueryPreprocessorTest::testInvalidQuery() {
	cout << "test invalid query" << endl;
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
		
	QueryTable qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a   ", &qt) == true);
	CPPUNIT_ASSERT( qt.mResultTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT( qt.mResultTable.getSynonymAtIndex(0)== "a");

	// Incomplete query	
    qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a;", &qt) == false);


	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; assign", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; assign;", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("  ", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; sele", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that pattern a(_, _)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that modifies(a, \"a\") such", &qt) == false);

	// Syntax incorrect in declaration and select
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a;; select a", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a select a", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select ab", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign ab!; select ab!", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign 4ab; select 4ab", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign aab; select; aab", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign aab; select! aab", &qt) == false);

	// Invalide relationship argument
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign aab; select aab such that modifies (aab, \"aab# \")", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign aab; select aab such that modifies (aab, aab)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign aab; select aab such that uses (\"aab\", aab)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that follows(\"a\", 1)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; constant c; select a such that parent(v, c)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign aab; select aab such that modifies (aab, aab)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign aab; select aab such that uses (\"aab\", aab)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that follows*(\"a\", 1)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that parent*(\"a\", 1)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that follow (0, 0)", &qt) == false);

	// invalid pattern
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern v(_,_)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern a('_','_')", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern a(_,'_x+y')", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern a(_,_\"x+y\")", &qt) == false);	

	// invalid relations followed by valid pattern etc.
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a such that Modifies(3, p) pattern a(_,_)", &qt) == false);	

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a such that Modifies(3, v) and Modifies (3, p) pattern a(_,_)", &qt) == false);	

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern a(_,__) such that Modifies(3, v) and Modifies (3, v) ", &qt) == false);	

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern a(_,_) and pattern a(_, __) such that Modifies(3, v) and Modifies (3, v) ", &qt) == false);	
}

void QueryPreprocessorTest::testMultiplePatterns() {
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	QueryTable qt = QueryTable();

	// Two patterns connected by and
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a pattern a(_, _) and a(_, \"  2+  3  \")", &qt) == true);	
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 2);

	// Three patterns connected by and
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a pattern a(_, _) and a(_, \"  2+  3  \") and a(_, \"  2+  a3  \")", &qt) == true);	
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 3);

	// Two pattern conditions
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a  pattern a(_, _) pattern a(_, \"  2+  3  \")", &qt) == true);	
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 2);

	// Pattern Pattern and
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a pattern a(_, _) pattern a(_, \"  2+  3  \") and a(_, \"  2+  a3  \")", &qt) == true);	
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 3);

	// pattern and pattern
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a pattern a(_, _) and a(_, \"  2+  3  \") pattern a(_, \"  2+  a3  \")", &qt) == true);	
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 3);
}

void QueryPreprocessorTest::testMultipleRelations() {
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	QueryTable qt = QueryTable();

	// Two relations connected by and
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a.stmt# such that Modifies(1, \" v \") and Modifies(3, \" d4 \") and Modifies(3, \" d4 \") and Modifies(3, \" d4 \")", &qt) == true);	
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 2); // after reduction

	// Three relations connected by and
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that Modifies(1, \" v \") and Modifies(3, \" d4 \") and uses(4, \" d4 \")", &qt) == true);	
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 3);	

	// Such that Rel and Rel Such that
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that Modifies(1, \" v \") and Modifies(3, \" d4 \") such that uses(4, \" d4 \")", &qt) == true);	
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 3);


	// Such that Rel Such that Rel and Rel
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that Modifies(1, \" v \") such that Modifies(3, \" d4 \") and uses(4, \" d4 \")", &qt) == true);	
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 3);
}

void QueryPreprocessorTest::testMeaninglessSpace() {

	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	QueryTable qt = QueryTable();

	// space occurs in double quoted strings
	// ---- space occurs in double quoted number strings
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a such that Modifies (\"  3  4  \", v)" , &qt) == false);	

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a such that Modifies (\"  34  \", v)" , &qt) == true);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a such that Modifies (\"  34\", v)" , &qt) == true);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a such that Modifies (\"34\", v)" , &qt) == true);

	// ---- space occurs in double quoted varName strings
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a such that Modifies (\"34\", \"  v  \")" , &qt) == true);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a such that Modifies (\"34\", \"  v5  \")" , &qt) == true);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a such that Modifies (\"34\", \"  v  5  \")" , &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a such that Modifies (\"34\", \"  v  5\")" , &qt) == false);

	// ---- space occurs in double quoted expression strings	
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern a(\"v5\", \"  x+  y   \")" , &qt) == true);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern a(\"v5\", \"  x+ 5 + y\")" , &qt) == true);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern a(\"v5\", \"  x+ 5 + y   +  y 5\")" , &qt) == false);

	// space occurs in pattern expression partial match underscore
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern a(\"v5\", _\"  x+ 5 + y   +  y\"_)" , &qt) == true);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern a(\"v5\", _     \"  x+ 5 + y   +  y\"_)" , &qt) == true);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable v; select a pattern a(\"v5\", _     \"  x+ 5 + y   +  y\"   _)" , &qt) == true);

}

void QueryPreprocessorTest::testMultipleRelPattern() {
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	QueryTable qt = QueryTable();

	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that Modifies(1, \" v \") pattern a(_, _) and a(_, \"  2+  3  \") such that Modifies(3, \" d4 \") and uses(4, \" d4 \") pattern a(_, _) ", &qt) == true);	
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 3);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 3);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that Modifies(1, \" v \") such that Modifies(3, \" d4 \") pattern a(_, _) and a(_, \"  2+  3  \") such that Modifies(3, \" d4 \") and uses(4, \" d4 \") pattern a(_, _) ", &qt) == true);	
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 3);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 3);

	// Some invalid ones
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; select a such that Modifies(1, \" v \") pattern a(_, _) and a(_, \"  2+  3  \") such that Modifies(3, \" d4 \") and pattern a(_, _) such that uses(4, \" d4 \")", &qt) == false);	
}

void QueryPreprocessorTest::testPreprocess()
{  
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	QueryTable qt = QueryTable();
	
	CPPUNIT_ASSERT( q.preprocess("assign b;", &qt) == false);
	CPPUNIT_ASSERT( q.preprocess("assign b; select a", &qt) == false);
	
	
	//The following test cases contain Follows, Follow*, Parents, Parents* only
	qt = QueryTable();
	CPPUNIT_ASSERT ( q.preprocess("assign a; Select a such that Follows( \" 12 \", 10)", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getDesignEntityType("a") == ASSIGN);
	CPPUNIT_ASSERT (qt.mSynonymTable.getDesignEntityType("aa") == INVALID_DESIGN_ENTITY);
	CPPUNIT_ASSERT (qt.mResultTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT ( q.preprocess("assign a; Select a such that Follows(a, 10)", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getDesignEntityType("a") == ASSIGN);
	CPPUNIT_ASSERT (qt.mSynonymTable.getDesignEntityType("aa") == INVALID_DESIGN_ENTITY);
	CPPUNIT_ASSERT (qt.mResultTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	

	qt= QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign b; Select a such that Follows(a, 10)", &qt) == false);
	

	qt= QueryTable();
	CPPUNIT_ASSERT( q.preprocess("stmt s; Select s such that Parent(s, 3)", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getDesignEntityType("a") == INVALID_DESIGN_ENTITY);
		CPPUNIT_ASSERT (qt.mResultTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt= QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; prog_line bbb9; Select a such that Follows(a, 10)", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 2);
	CPPUNIT_ASSERT (qt.mSynonymTable.getDesignEntityType("a") == ASSIGN);
	CPPUNIT_ASSERT (qt.mSynonymTable.getDesignEntityType("bbb9") == PROG_LINE);

	qt= QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; variable bbb9; Select bbb9 such that Follows(bbb9, 10)", &qt) == false);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 2);

	qt= QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a##; variable bbb9; Select a## such that Follows( _ , \"  10 \")", &qt) == true);
	CPPUNIT_ASSERT( qt.mRelClauseTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 2);
	int t = qt.mRelClauseTable.getRelationshipEntry(0).relationshipArguments[0].compare("_");
	CPPUNIT_ASSERT( t == 0);
	t = qt.mRelClauseTable.getRelationshipEntry(0).relationshipArguments[1].compare("10");
	CPPUNIT_ASSERT( t == 0);

	qt=QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, b, c; Select a such that Parent* (_, 10)", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 3);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, b, c; Select a such that Parent* (_, _)", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 3);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; variable d  ; Select a such that follows* (_, _)", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 4);
	
	// The following tests modifies, uses, pattern 
	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; variable d  ; Select d pattern a (_,_) such that modifies (4,d)", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 4);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; variable d  ; Select d pattern a (\"x\", _) such that modifies (4,_)", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 4);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; variable d  ; Select d pattern a (d, _\" x+y\"_) such that modifies (a, d)", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 4);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a0, a1; prog_line p; stmt s; Select s pattern a1 (\"x\", _\"y  +   z\"_) such that Modifies(s, \"x\")", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 4);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; variable d  ; Select d    such that modifies (a, d) such that uses(a, d)  pattern a (d, \"x+y\")", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 4);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 2);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; variable d  ; Select d such that modifies (_, _) pattern a (d, \"x+y\") ", &qt) == false);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; variable d  ; Select d such that modifies (-1, d) pattern a (d, \"_x+y\") ", &qt) == false);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; variable d  ; Select d pattern a (d, _\" (x+y)\"_) such that modifies (a, d)", &qt) == true);

	
	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; variable d  ; Select d pattern a (d, _  \" ( x+ 0y)\"  _  ) such that modifies (a, d)", &qt) == false);
	
	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; while w; variable d  ; Select d pattern w (d, _) such that modifies (a, d)", &qt) == true);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; while w; variable d  ; Select d pattern w (\"d\", _) such that modifies (a, d)", &qt) == true);
	CPPUNIT_ASSERT (qt.mSynonymTable.getNumberOfSynonyms() == 5);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mResultTable.getNumberOfEntries() == 1);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; while w; variable d  ; Select d pattern w (d0, _) such that modifies ( \"  a \", \"  d\")", &qt) == false);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; while w; variable d  ; Select d pattern d (d, _) such that modifies (a, d)", &qt) == false);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; while w; variable d  ; Select d pattern w (d, d) such that modifies (a, d)", &qt) == false);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; while w; variable d ; constant q ; Select q pattern a (d, _\"d\"_) such that modifies (a, d)", &qt) == true);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a, ba, ca; while w; variable d ; constant q ; Select q pattern a (_, _\"d\"_) such that modifies (a, d)", &qt) == true);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);

	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a ; while w; variable d ; constant q ; Select q pattern a (_, _\" d \"_) such that modifies (a, d) pattern a (_, _)", &qt) == true);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 2);


	qt =QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a ; while w; variable d ; constant q ; Select q pattern a (_, _\"d\"_) such that modifies (a, d) pattern a (   _   , _\"x   +    y\"_)", &qt) == true);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 2);
	return;
}


void   QueryPreprocessorTest::testWithClause() {
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	ConstraintEntry entry;
	QueryTable qt;
	
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; Select p such that uses (p, v) with p.procname=\"Temp\"", &qt) == true);
	CPPUNIT_ASSERT (qt.mConstraintTable.getNumberOfEntries() == 1);
	entry = qt.mConstraintTable.getConstraintEntry(0);
	CPPUNIT_ASSERT (entry.constraintType == WITH_STRING);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[0] == SYNONYM_PROCEDURE);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[1] == STRING);
	CPPUNIT_ASSERT (entry.constraintArguments[0] == "p");
	CPPUNIT_ASSERT (entry.constraintArguments[1] == "Temp");
	CPPUNIT_ASSERT (entry.constraintAttrTypes[0] == PROC_NAME);
	CPPUNIT_ASSERT (entry.constraintAttrTypes[1] == NONE);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; Select p such that uses (p, v) with p.procName = v.varName", &qt) == true);
	CPPUNIT_ASSERT (qt.mConstraintTable.getNumberOfEntries() == 1);
	entry = qt.mConstraintTable.getConstraintEntry(0);
	CPPUNIT_ASSERT (entry.constraintType == WITH_STRING);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[0] == SYNONYM_PROCEDURE);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[1] == SYNONYM_VARIABLE);
	CPPUNIT_ASSERT (entry.constraintArguments[0] == "p");
	CPPUNIT_ASSERT (entry.constraintArguments[1] == "v");
	CPPUNIT_ASSERT (entry.constraintAttrTypes[0] == PROC_NAME);
	CPPUNIT_ASSERT (entry.constraintAttrTypes[1] == VAR_NAME);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("while w; if ifstat; call c; assign a; procedure p; Select c such that Parent*(w, c) and Parent*(ifstat, a) pattern a(_, _\"1\"_) with c.procName = p.procName", &qt) == true);
	CPPUNIT_ASSERT (qt.mConstraintTable.getNumberOfEntries() == 1);
	entry = qt.mConstraintTable.getConstraintEntry(0);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 2);
	CPPUNIT_ASSERT (entry.constraintType == WITH_STRING);
	CPPUNIT_ASSERT (entry.constraintArguments[0] == "c");
	CPPUNIT_ASSERT (entry.constraintArguments[1] == "p");
	CPPUNIT_ASSERT (entry.constraintAttrTypes[0] == PROC_NAME);
	CPPUNIT_ASSERT (entry.constraintAttrTypes[1] == PROC_NAME);
	

	qt = QueryTable();
	CPPUNIT_ASSERT ( q.preprocess("stmt s1,s2; constant c1,c2; Select c1 with s1.stmt#=s2.stmt# and s1.stmt#=s2.stmt# and s1.stmt#=s2.stmt# and s1.stmt#=s2.stmt#", &qt) == true);

	

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; call c; Select p with c.procName =p.procName  such that uses (p, v)", &qt) == true);
	CPPUNIT_ASSERT (qt.mConstraintTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	entry = qt.mConstraintTable.getConstraintEntry(0);
	CPPUNIT_ASSERT (entry.constraintType == WITH_STRING);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[0] == SYNONYM_CALL);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[1] == SYNONYM_PROCEDURE);
	CPPUNIT_ASSERT (entry.constraintArguments[0] == "c");
	CPPUNIT_ASSERT (entry.constraintArguments[1] == "p");
	CPPUNIT_ASSERT (entry.constraintAttrTypes[0] == PROC_NAME);
	CPPUNIT_ASSERT (entry.constraintAttrTypes[1] == PROC_NAME);


	// 2 With clause connected by and
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; call c; Select p with c.procName = p.procName and c.stmt#= \"13\"", &qt) == true);
	CPPUNIT_ASSERT (qt.mConstraintTable.getNumberOfEntries() == 2);
	entry = qt.mConstraintTable.getConstraintEntry(1);
	CPPUNIT_ASSERT (entry.constraintType == WITH_INTEGER);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[0] == SYNONYM_CALL);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[1] == INTEGER);
	CPPUNIT_ASSERT (entry.constraintArguments[0] == "c");
	CPPUNIT_ASSERT (entry.constraintArguments[1] == "13");
	CPPUNIT_ASSERT (entry.constraintAttrTypes[0] == STMT_INDEX);
	CPPUNIT_ASSERT (entry.constraintAttrTypes[1] == NONE);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p;  Select p with 12 = \"13\"", &qt) == true);
	CPPUNIT_ASSERT (qt.mConstraintTable.getNumberOfEntries() == 1);
	entry = qt.mConstraintTable.getConstraintEntry(0);
	CPPUNIT_ASSERT (entry.constraintType == WITH_INTEGER);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[0] == INTEGER);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[1] == INTEGER);
	CPPUNIT_ASSERT (entry.constraintArguments[0] == "12");
	CPPUNIT_ASSERT (entry.constraintArguments[1] == "13");
	CPPUNIT_ASSERT (entry.constraintAttrTypes[0] == NONE);
	CPPUNIT_ASSERT (entry.constraintAttrTypes[1] == NONE);

	
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; constant c; stmt s;  Select p with s = c.value", &qt) == false );
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; constant c; prog_line pp;  Select p with pp = c.value", &qt) == true );

	qt = QueryTable();
	//CPPUNIT_ASSERT( q.preprocess("procedure p; constant c; prog_line pp;  Select p with pp.stmt# = c.value", &qt) == true );

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; constant c;  Select p with 12 = c.value", &qt) == true );
	CPPUNIT_ASSERT (qt.mConstraintTable.getNumberOfEntries() == 1);
	entry = qt.mConstraintTable.getConstraintEntry(0);
	CPPUNIT_ASSERT (entry.constraintType == WITH_INTEGER);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[0] == INTEGER);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[1] == SYNONYM_CONSTANT);
	CPPUNIT_ASSERT (entry.constraintArguments[0] == "12");
	CPPUNIT_ASSERT (entry.constraintArguments[1] == "c");
	CPPUNIT_ASSERT (entry.constraintAttrTypes[0] == NONE);
	CPPUNIT_ASSERT (entry.constraintAttrTypes[1] == VALUE);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; constant c;  Select p with \"a2\" = \"cvalue\"", &qt) == true );
	CPPUNIT_ASSERT (qt.mConstraintTable.getNumberOfEntries() == 1);
	entry = qt.mConstraintTable.getConstraintEntry(0);
	CPPUNIT_ASSERT (entry.constraintType == WITH_STRING);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[0] == STRING);
	CPPUNIT_ASSERT (entry.constraintArgumentTypes[1] == STRING);
	CPPUNIT_ASSERT (entry.constraintArguments[0] == "a2");
	CPPUNIT_ASSERT (entry.constraintArguments[1] == "cvalue");
	CPPUNIT_ASSERT (entry.constraintAttrTypes[0] == NONE);
	CPPUNIT_ASSERT (entry.constraintAttrTypes[1] == NONE);

	// invalid cases
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; Select p with 15 = c.value", &qt) == false );

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; constant c; Select p with 15 = c.value with 16 = c", &qt) == false );

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; constant c; Select p with 15 = c.value with p.procName = c.value", &qt) == false );

}

void QueryPreprocessorTest::testInclCallSynonym() {
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	
	QueryTable qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; Select p such that uses (p, v)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
}

void QueryPreprocessorTest::testIfPattern() {
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	
	QueryTable qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("if i; variable v; Select i pattern i(v,_,_)" , &qt) == true);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("if i; variable v; Select i pattern i( \"v\", _,_)" , &qt) == true);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("if i; variable v; Select i pattern i( \"v\", _)" , &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("if i; variable v; Select i pattern v( \"v\", _, _)" , &qt) == false);
}

void   QueryPreprocessorTest::testBoolean() {
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	
	QueryTable qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select BOOLEAN such that next(n1, n2)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mResultTable.getNumberOfEntries() == 0);	
}

void   QueryPreprocessorTest::testNextAffect() {
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	
	QueryTable qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select p such that next(n1, n2)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mResultTable.getNumberOfEntries() == 1);	

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select p such that next(v, n2)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select p such that next*(_, n2)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("stmt n; Select n such that Next(56, n)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipType == RelationshipType::NEXT);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArgumentTypes[0]== RelationshipArgumentType::INTEGER);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArgumentTypes[1]== RelationshipArgumentType::SYNONYM_STMT);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("stmt n; Select n such that Next*(38, n)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipType == RelationshipType::NEXT_STAR);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArgumentTypes[0]== RelationshipArgumentType::INTEGER);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArgumentTypes[1]== RelationshipArgumentType::SYNONYM_STMT);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("stmt n; Select n such that Affects*(38, n)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipType == RelationshipType::AFFECTS_STAR);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArgumentTypes[0]== RelationshipArgumentType::INTEGER);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArgumentTypes[1]== RelationshipArgumentType::SYNONYM_STMT);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("stmt n; Select n such that Affects(38, _)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipType == RelationshipType::AFFECTS);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArgumentTypes[0]== RelationshipArgumentType::INTEGER);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArgumentTypes[1]== RelationshipArgumentType::SYNONYM_STMT);

	
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a; Select a such that Affects(a, _)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);


}


void   QueryPreprocessorTest::testTuples() {
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	
	QueryTable qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select <p> such that next(n1, n2)", &qt) == true);
	CPPUNIT_ASSERT( qt.mResultTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT( qt.mResultTable.getSynonymAtIndex(0) == "p");

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select <p.procName> such that next(n1, n2)", &qt) == true);
	CPPUNIT_ASSERT( qt.mResultTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT( qt.mResultTable.getSynonymAtIndex(0) == "p");
	CPPUNIT_ASSERT( qt.mResultTable.getAttributeAtIndex(0) == PROC_NAME);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select <p.varName> such that next(n1, n2)", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("call c;procedure p; variable v; stmt n1, n2; Select <c.procName> such that next(n1, n2)", &qt) == true);
	CPPUNIT_ASSERT( qt.mResultTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT( qt.mResultTable.getSynonymAtIndex(0) == "c");
	CPPUNIT_ASSERT( qt.mResultTable.getAttributeAtIndex(0) == PROC_NAME);



	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select <p, n1> such that next(n1, n2)", &qt) == true);
	CPPUNIT_ASSERT( qt.mResultTable.getNumberOfEntries() == 2);
	CPPUNIT_ASSERT( qt.mResultTable.getSynonymAtIndex(0) == "p");
	CPPUNIT_ASSERT( qt.mResultTable.getAttributeAtIndex(0) == NONE);
	CPPUNIT_ASSERT( qt.mResultTable.getSynonymAtIndex(1) == "n1");
	CPPUNIT_ASSERT( qt.mResultTable.getAttributeAtIndex(1) == NONE);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select <p.procName, n1.stmt#> such that next(n1, n2)", &qt) == true);
	CPPUNIT_ASSERT( qt.mResultTable.getNumberOfEntries() == 2);
	CPPUNIT_ASSERT( qt.mResultTable.getSynonymAtIndex(0) == "p");
	CPPUNIT_ASSERT( qt.mResultTable.getAttributeAtIndex(0) == PROC_NAME);
	CPPUNIT_ASSERT( qt.mResultTable.getSynonymAtIndex(1) == "n1");
	CPPUNIT_ASSERT( qt.mResultTable.getAttributeAtIndex(1) == STMT_INDEX);



	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select <p, n1, n3> such that next(n1, n2)", &qt) == false);
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select <p, n1 such that next(n1, n2)", &qt) == false);
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select <p, n1", &qt) == false);
	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select <", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; stmt n1, n2; Select < p, n1, n2>", &qt) == true);
	CPPUNIT_ASSERT( qt.mResultTable.getNumberOfEntries() == 3);
	CPPUNIT_ASSERT( qt.mResultTable.getSynonymAtIndex(0) == "p");
	CPPUNIT_ASSERT( qt.mResultTable.getSynonymAtIndex(1) == "n1");
	CPPUNIT_ASSERT( qt.mResultTable.getSynonymAtIndex(2) == "n2");



}

void   QueryPreprocessorTest::testInclProcSynonym() {
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	
	QueryTable qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; variable v; Select p such that uses (p, v)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; Select p such that uses (p, \"d\")", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; Select p such that modifies (p, \"d\")", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipType == RelationshipType::MODIFIES_PROC == true);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p; Select p such that uses (_, \"d\")", &qt) == false);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 0);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p1, p2; Select p1 such that calls (_, p2)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p1, p2; Select p1 such that calls (_, _)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p1, p2; Select p1 such that calls* (_, _)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p1, p2; Select p1 such that calls* (p1,p2)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p1, p2; Select p1 such that calls (p2, \"oneProcedure\")", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p1, p2; variable v; Select v such that modifies (p2, v)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p1, p2; variable v; Select v such that modifies (\"p2\", v)", &qt) == true);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("procedure p1, p2; Select p1 such that uses (4,p2)", &qt) == false);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 0);
	
	return ;
}

void   QueryPreprocessorTest::testContains() {
	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	
	QueryTable qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("plus add; variable v; Select add", &qt) == false);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("plus add; variable v; assign a; stmtLst s1; Select a such that contains(s1, a)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	RelationshipEntry e = qt.mRelClauseTable.getRelationshipEntry(0);
	CPPUNIT_ASSERT (e.relationshipArguments[0] == "s1");
	CPPUNIT_ASSERT (e.relationshipArguments[1] == "a");
	CPPUNIT_ASSERT (e.relationshipArgumentTypes[0] == SYNONYM_STMTLST);
	CPPUNIT_ASSERT (e.relationshipArgumentTypes[1] == SYNONYM_ASSIGN);
	CPPUNIT_ASSERT (e.relationshipType == CONTAINS);
	
	qt= QueryTable();
	CPPUNIT_ASSERT( q.preprocess("plus add; variable v; assign a; Select v such that contains(a, add)", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	e = qt.mRelClauseTable.getRelationshipEntry(0);
	CPPUNIT_ASSERT (e.relationshipArguments[0] == "a");
	CPPUNIT_ASSERT (e.relationshipArguments[1] == "add");
	CPPUNIT_ASSERT (e.relationshipArgumentTypes[0] == SYNONYM_ASSIGN);
	CPPUNIT_ASSERT (e.relationshipArgumentTypes[1] == SYNONYM_PLUS);
	CPPUNIT_ASSERT (e.relationshipType == CONTAINS);
}

void   QueryPreprocessorTest::testQueryReduction() {

	PKB *pkb = new PKB();
	QueryExpressionParser ep = QueryExpressionParser(pkb);
	QueryPreprocessor q = QueryPreprocessor(&ep);
	
	QueryTable qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a1, a2, a3; variable v; select a3 such that modifies(a1, v) and modifies(a2, v) and modifies(a3, v) with a1.stmt# = a2.stmt# and a2.stmt# = a3.stmt#", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mConstraintTable.getNumberOfEntries() == 0);
	CPPUNIT_ASSERT (qt.mResultTable.getNumberOfEntries() == 1);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a1, a2, a3; variable v; select <a1, a2, a3> such that modifies(a1, v) and modifies(a2, \"v\") and modifies(a3, v) with a1.stmt# = a2.stmt# and a2.stmt# = a3.stmt#", &qt) == true);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 2);
	RelationshipEntry e = qt.mRelClauseTable.getRelationshipEntry(0);
	CPPUNIT_ASSERT(e.relationshipArguments[0] == "a1");
	CPPUNIT_ASSERT(e.relationshipArgumentTypes[0] == SYNONYM_ASSIGN);
	CPPUNIT_ASSERT(e.relationshipArguments[1] == "v");
	CPPUNIT_ASSERT(e.relationshipArgumentTypes[1] == SYNONYM_VARIABLE);

	e = qt.mRelClauseTable.getRelationshipEntry(1);
	CPPUNIT_ASSERT(e.relationshipArguments[0] == "a1");
	CPPUNIT_ASSERT(e.relationshipArgumentTypes[0] == SYNONYM_ASSIGN);
	CPPUNIT_ASSERT(e.relationshipArguments[1] == "v");
	CPPUNIT_ASSERT(e.relationshipArgumentTypes[1] == STRING);


	CPPUNIT_ASSERT (qt.mConstraintTable.getNumberOfEntries() == 0);
	CPPUNIT_ASSERT (qt.mResultTable.getNumberOfEntries() == 3);
	/*
	cout << (qt.mResultTable.getSynonymAtIndex(0))<< endl;
	cout << (qt.mResultTable.getSynonymAtIndex(1))<< endl;
	cout << (qt.mResultTable.getSynonymAtIndex(2))<< endl;
	*/
	
	CPPUNIT_ASSERT (qt.mResultTable.getSynonymAtIndex(0).compare("a1") ==0);
	CPPUNIT_ASSERT (qt.mResultTable.getSynonymAtIndex(1).compare("a1") ==0);
	CPPUNIT_ASSERT (qt.mResultTable.getSynonymAtIndex(2).compare("a1") ==0);

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a1, a2, a3; variable v; select <a1, a2, a3> such that modifies(a1, v) and modifies(a2, \"v\") and modifies(a3, v) with a1.stmt# = a2.stmt# and a2.stmt# = a3.stmt# pattern a3(v, _) ", &qt) == true);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mPatternTable.getPatternEntryAtIndex(0).synonym == "a1");

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a1, a2, a3; variable v; select <a1.stmt#, a2, a3> such that modifies(a1, v) and modifies(a2, \"v\") and modifies(a3, v) with a1.stmt# = 15 and a2.stmt# = 15 pattern a3(v, _) ", &qt) == true);
	// after reduction: assign a1, a2, a3; variable v; select <a1, a1, a3> such that modifies(a1, v) and modifies(a1, "v") modifies(a3, v) with a1.stmt# = 15 pattern a3(v, _)
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mPatternTable.getPatternEntryAtIndex(0).synonym == "a3");
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 3);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArguments[0] == "a1");
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(1).relationshipArguments[0] == "a3");
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(2).relationshipArguments[0] == "a1");

	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArguments[1] == "v");
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(1).relationshipArguments[1] == "v");
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(2).relationshipArguments[1] == "v");

	
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArgumentTypes[1] == SYNONYM_VARIABLE);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(1).relationshipArgumentTypes[1] == SYNONYM_VARIABLE);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(2).relationshipArgumentTypes[1] == STRING);

	CPPUNIT_ASSERT (qt.mResultTable.getSynonymAtIndex(0) == "a1");
	CPPUNIT_ASSERT (qt.mResultTable.getAttributeAtIndex(0) == STMT_INDEX);
	CPPUNIT_ASSERT (qt.mResultTable.getSynonymAtIndex(1) == "a1");
	CPPUNIT_ASSERT (qt.mResultTable.getAttributeAtIndex(1) == NONE);
	CPPUNIT_ASSERT (qt.mResultTable.getSynonymAtIndex(2) == "a3");

	qt = QueryTable();
	// with a1.stmt# = 15 and a2.stmt# = 15 and a3.stmt# = a4.stmt# and s4.stmt# = a1.stmt# and v1.varName = v2.varName 
	CPPUNIT_ASSERT( q.preprocess("assign a1, a2, a3, a4; variable v, v1, v2; select <a1, a2, a3> such that modifies(a1, v) and uses(a2, v) and follows(a2, a3) such that next(a3, a4)  with a1.stmt# = 15 and a2.stmt# = 15 and a3.stmt# = a4.stmt# and a4.stmt# = a1.stmt# and v1.varName = v2.varName pattern a1(v, _)", &qt) == true);
	// after reduction: "assign a1, a2, a3, a4; variable v, v1, v2; select <a3, a3, a3> such that modifies(a3, v) and uses(a3, v) and follows(a3, a3) such that next(a3, a3)  with a3.stmt# = 15 and a3.stmt# = 15 pattern a3(v, _)"
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mPatternTable.getPatternEntryAtIndex(0).synonym == "a3");
	CPPUNIT_ASSERT (qt.mConstraintTable.getNumberOfEntries() == 2);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 4);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArguments[0] == "a3");
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(1).relationshipArguments[0] == "a3");
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(2).relationshipArguments[0] == "a3");

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a1, a2, a3, a4; variable v, v1, v2; select <a1, a2, a3> such that modifies(a1, v) and uses(a2, v) and follows(a2, a3) such that next(a3, a4)  with a1.stmt# = 15 and  15 =a2.stmt# and a3.stmt# = a4.stmt# and a4.stmt# = a1.stmt# and v1.varName = v2.varName pattern a1(v, _)", &qt) == true);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mPatternTable.getPatternEntryAtIndex(0).synonym == "a3");
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 4);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArguments[0] == "a3");
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(1).relationshipArguments[0] == "a3");
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(2).relationshipArguments[0] == "a3");

	qt = QueryTable();
	CPPUNIT_ASSERT( q.preprocess("assign a1, a2, a3, a4; select a1 such that next*(a1, a2) and next*(a2, a3) with a1.stmt#=a2.stmt# and a2.stmt# = a3.stmt#", &qt) == true);
	CPPUNIT_ASSERT (qt.mPatternTable.getNumberOfEntries() == 0);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getNumberOfEntries() == 1);
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArguments[0] == "a1");
	CPPUNIT_ASSERT (qt.mRelClauseTable.getRelationshipEntry(0).relationshipArguments[1] == "a1");


}